use std::sync::Arc;

use axum::{
    extract::{Path, Query},
    response::IntoResponse,
    routing::{get, put},
    Extension, Router,
};
use casbin::MgmtApi;
use common::page::{PageInfo, PageParams};

use crate::{
    auth_manager::AuthManager, error::{AppError, AppResult}, extract::{Json, ValJson}, AppState
};

pub fn get_route<S>() -> Router<S>
where
    S: Sync + Send + Clone + 'static,
{
    Router::<S>::new()
        .route("/", get(list).post(create))
        .route("/all", get(all))
        .route(
            "/resource/:id",
            get(fetch_role_resource).put(update_role_resource),
        )
        .route("/:id", put(update).delete(delete))
}

pub async fn all(Extension(state): Extension<Arc<AppState>>) -> AppResult<impl IntoResponse> {
    ane_system::dao::Role::all(state.db())
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn list(
    Extension(state): Extension<Arc<AppState>>,
    Query(page_params): Query<PageParams>,
) -> AppResult<impl IntoResponse> {
    ane_system::dao::Role::list(state.db(), page_params.zero_page(), page_params.page_size)
        .await
        .map(|(total, datas)| PageInfo::new(datas, &page_params, total))
        .map(Json)
        .map_err(Into::into)
}

pub async fn create(
    Extension(state): Extension<Arc<AppState>>,
    ValJson(data): ValJson<ane_system::dao::role::RoleDo>,
) -> AppResult<impl IntoResponse> {
    ane_system::dao::Role::create(state.db(), data)
        .await
        .map(Json)
        .map_err(Into::into)
}

/// 更新role信息
///
/// 当status改变时, 需要更新casbin
pub async fn update(
    Extension(state): Extension<Arc<AppState>>,
    Extension(ane_system_manager): Extension<AuthManager>,
    Path(id): Path<i32>,
    ValJson(data): ValJson<ane_system::dao::RoleDo>,
) -> AppResult<impl IntoResponse> {
    let db = state.db();
    let role = ane_system::dao::Role::fetch_one(db, id)
        .await?
        .ok_or(AppError::NotFount)?;
    let need_update = role.status != data.status;
    let status = data.status;
    let update_data_scope = role.data_scope != data.data_scope;
    let res = ane_system::dao::Role::update(db, id, data)
        .await
        .map(Json)
        .map_err(Into::into);
    if update_data_scope {
        let ids = ane_system::dao::Role::fetch_user(db, id).await?;
        ane_system_manager.clean_users_cache(ids).await;
    }
    if need_update {
        if status {
            // 启用
            let resources = ane_system::dao::Role::fetch_resource(db, id).await?;
            let role_id = format!("role_{id}");
            let mut enforcer = state.enforcer().lock().await;
            let policies = resources
                .into_iter()
                .filter(|f| f.status)
                .map(|f| vec![role_id.clone(), f.path, f.method])
                .collect::<Vec<_>>();
            enforcer.add_policies(policies).await.unwrap();
            // enforcer.save_policy().await.unwrap();
        } else {
            // 停用
            let role_id = format!("role_{id}");
            let mut enforcer = state.enforcer().lock().await;
            enforcer
                .remove_filtered_policy(0, vec![role_id])
                .await
                .unwrap();
            // enforcer.save_policy().await.unwrap();
        }
    }
    res
}

/// 删除role信息，更新casbin
pub async fn delete(
    Extension(state): Extension<Arc<AppState>>,
    Path(id): Path<i32>,
) -> AppResult<impl IntoResponse> {
    let res = ane_system::dao::Role::delete(state.db(), id)
        .await
        .map_err(Into::into)
        .map(|_| "Ok");
    {
        let role_id = format!("role_{id}");
        let mut enforcer = state.enforcer().lock().await;
        enforcer
            .remove_filtered_policy(0, vec![role_id])
            .await
            .unwrap();
        // enforcer.save_policy().await.unwrap();
    }
    res
}

pub async fn fetch_role_resource(
    Extension(state): Extension<Arc<AppState>>,
    Path(id): Path<i32>,
) -> AppResult<impl IntoResponse> {
    ane_system::dao::Role::fetch_resource(state.db(), id)
        .await
        .map(Json)
        .map_err(Into::into)
}

/// 更新一个role的资源，同时更新casbin
pub async fn update_role_resource(
    Extension(state): Extension<Arc<AppState>>,
    Path(id): Path<i32>,
    Json(res): Json<Vec<i32>>,
) -> AppResult<impl IntoResponse> {
    let res = ane_system::dao::Role::update_resource(state.db(), id, res)
        .await
        .map(Json)
        .map_err(Into::into);
    let role_id = format!("role_{id}");
    let policies = crate::utils::casbin_policies(state.db(), Some(id), None).await?;
    {
        let mut enforcer = state.enforcer().lock().await;
        enforcer
            .remove_filtered_policy(0, vec![role_id.clone()])
            .await
            .unwrap();
        enforcer.add_policies(policies).await.unwrap();
        // enforcer.save_policy().await.unwrap();
    }
    res
}
